using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.Package;
using NBusiness.VisualStudio.Automation;
using Microsoft.VisualStudio.Package.Automation;

using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants;
using VsCommands = Microsoft.VisualStudio.VSConstants.VSStd97CmdID;
using VsCommands2K = Microsoft.VisualStudio.VSConstants.VSStd2KCmdID;
using System.Globalization;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
using System.Diagnostics;
using System.IO;

namespace NBusiness.VisualStudio
{
    class ESharpFileNode : FileNode
    {
        #region fields
        private OAVSProjectItem vsProjectItem;
        private SelectionElementValueChangedListener selectionChangedListener;
        private OAESharpFileItem automationObject;
        #endregion
        
        #region properties
        /// <summary>
        /// Returns bool indicating whether this node is of subtype "Form"
        /// </summary>
        public bool IsFormSubType
        {
            get
            {
                string result = this.ItemNode.GetMetadata(ProjectFileConstants.SubType);
                if (!String.IsNullOrEmpty(result) && string.Compare(result, ProjectFileAttributeValue.Form, true, CultureInfo.InvariantCulture) == 0)
                    return true;
                else
                    return false;
            }
        }
        /// <summary>
        /// Returns the SubType of an Iron Python FileNode. It is 
        /// </summary>
        public string SubType
        {
            get
            {
                return this.ItemNode.GetMetadata(ProjectFileConstants.SubType);
            }
            set
            {
                this.ItemNode.SetMetadata(ProjectFileConstants.SubType, value);
            }
        }

        protected internal VSLangProj.VSProjectItem VSProjectItem
        {
            get
            {
                if (null == vsProjectItem)
                {
                    vsProjectItem = new OAVSProjectItem(this);
                }
                return vsProjectItem;
            }
        }
        #endregion
        
        #region ctors
        internal ESharpFileNode(ProjectNode root, ProjectElement e)
            : base(root, e)
        {
            selectionChangedListener = new SelectionElementValueChangedListener(new ServiceProvider((IOleServiceProvider)root.GetService(typeof(IOleServiceProvider))), root);
            selectionChangedListener.Init();
        }
        #endregion

        #region overridden properties

        internal override object Object
        {
            get
            {
                return this.VSProjectItem;
            }
        }
        #endregion

        #region overridden methods
        //protected override FileNode  RenameFileNode(string oldFileName, string newFileName, uint newParentId)
        //{
        //    if (string.Compare(oldFileName, newFileName, StringComparison.InvariantCulture) == 0)
        //    {
        //        // We do not want to rename the same file
        //        return null;
        //    }

        //    //Clean up all this crud
        //    this.OnItemDeleted();
        //    this.Parent.RemoveChild(this);

        //    // Since this node has been removed all of its state is zombied at this point
        //    // Do not call virtual methods after this point since the object is in a deleted state.

        //    string[] file = new string[1];
        //    file[0] = newFileName;
        //    VSADDRESULT[] result = new VSADDRESULT[1];
        //    Guid emptyGuid = Guid.Empty;
        //    ErrorHandler.ThrowOnFailure(this.ProjectMgr.AddItemWithSpecific(newParentId, VSADDITEMOPERATION.VSADDITEMOP_OPENFILE, null, 0, file, IntPtr.Zero, 0, ref emptyGuid, null, ref emptyGuid, result));

        //    FileNode childAdded = this.ProjectMgr.FindChild(newFileName) as FileNode;
        //    Debug.Assert(childAdded != null, "Could not find the renamed item in the hierarchy");
        //    // Update the itemid to the newly added.
        //    this.ID = childAdded.ID;

        //    // Remove the item created by the add item. We need to do this otherwise we will have two items.
        //    // Please be aware that we have not removed the ItemNode associated to the removed file node from the hierrachy.
        //    // What we want to achieve here is to reuse the existing build item. 
        //    // We want to link to the newly created node to the existing item node and addd the new include.

        //    //temporarily keep properties from new itemnode since we are going to overwrite it
        //    string newInclude = childAdded.ItemNode.Item.Include;
        //    string dependentOf = childAdded.ItemNode.GetMetadata(ProjectFileConstants.DependentUpon);
        //    childAdded.ItemNode.RemoveFromProjectFile();

        //    // Assign existing msbuild item to the new childnode
        //    childAdded.ItemNode = this.ItemNode;
        //    childAdded.ItemNode.Item.Name = this.ItemNode.ItemName;
        //    childAdded.ItemNode.Item.Include = newInclude;

        //    FileNode node = childAdded;
        //    if (!string.IsNullOrEmpty(dependentOf) &&
        //        newInclude.StartsWith(Path.GetFileNameWithoutExtension(dependentOf)))
        //    {
        //        childAdded.ItemNode.SetMetadata(ProjectFileConstants.DependentUpon, dependentOf);
        //    }
        //    else
        //    {
        //        childAdded.ItemNode.SetMetadata(ProjectFileConstants.DependentUpon, null);
        //        HierarchyNode newParent = childAdded.Parent.Parent;
        //        childAdded.Parent.RemoveChild(childAdded);

        //        //Move the item up if it's name isn't a subset of the parents anymore.
        //        if (newParent != null)
        //        {
        //            ESharpFileNode independent = new ESharpFileNode(this.ProjectMgr, childAdded.ItemNode);
        //            newParent.AddChild(independent);
        //            HierarchyNode hn = childAdded.FirstChild;
        //            while (hn != null)
        //            {
        //                node.AddChild(hn);
        //                hn = hn.NextSibling;
        //            }
        //            node = independent;
        //            node.ID = childAdded.ID;
        //        }
        //    }

        //    node.ItemNode.RefreshProperties();

        //    //Update the new document in the RDT.
        //    DocumentManager.RenameDocument(this.ProjectMgr.Site, oldFileName, newFileName, node.ID);

        //    //Select the new node in the hierarchy
        //    IVsUIHierarchyWindow uiWindow = UIHierarchyUtilities.GetUIHierarchyWindow(this.ProjectMgr.Site, SolutionExplorer);
        //    uiWindow.ExpandItem(this.ProjectMgr, this.ID, EXPANDFLAGS.EXPF_SelectItem);

        //    //Update FirstChild
        //    node.FirstChild = this.FirstChild;

        //    //Update ChildNodes
        //    SetNewParentOnChildNodes(node);
        //    RenameChildNodes(node);

        //    return node;
        //}


        protected override NodeProperties CreatePropertiesObject()
        {
            ESharpFileNodeProperties properties = new ESharpFileNodeProperties(this);
            properties.OnCustomToolChanged += new EventHandler<HierarchyNodeEventArgs>(OnCustomToolChanged);
            properties.OnCustomToolNameSpaceChanged += new EventHandler<HierarchyNodeEventArgs>(OnCustomToolNameSpaceChanged);
            return properties;
        }

        public override int Close()
        {
            if (selectionChangedListener != null)
                selectionChangedListener.Dispose();
            return base.Close();
        }

        /// <summary>
        /// Returs an Iron Python FileNode specific object implmenting DTE.ProjectItem
        /// </summary>
        /// <returns></returns>
        public override object GetAutomationObject()
        {
            if (null == automationObject)
            {
                automationObject = new OAESharpFileItem(this.ProjectMgr.GetAutomationObject() as OAProject, this);
            }
            return automationObject;
        }

        public override int ImageIndex
        {
            get
            {
                if (IsFormSubType)
                {
                    return (int)ProjectNode.ImageName.WindowsForm;
                }
                if (this.FileName.ToLower().EndsWith(SR.GetString("ESharpFileExtension")))
                {
                    return ESharpProjectNode.ImageOffset + (int)ESharpProjectNode.ESharpImageName.EsFile;
                }
                return base.ImageIndex;
            }
        }

        /// <summary>
        /// Open a file depending on the SubType property associated with the file item in the project file
        /// </summary>
        protected override void DoDefaultAction()
        {
            FileDocumentManager manager = this.GetDocumentManager() as FileDocumentManager;
            Debug.Assert(manager != null, "Could not get the FileDocumentManager");

            Guid viewGuid = (IsFormSubType ? NativeMethods.LOGVIEWID_Designer : NativeMethods.LOGVIEWID_Code);
            IVsWindowFrame frame;
            manager.Open(false, false, viewGuid, out frame, WindowFrameShowAction.Show);
        }

        protected override int ExecCommandOnNode(Guid guidCmdGroup, uint cmd, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
        {
            Debug.Assert(this.ProjectMgr != null, "The EntityFileNode has no project manager");

            if (this.ProjectMgr == null)
            {
                throw new InvalidOperationException();
            }

            if (guidCmdGroup == ESharpMenus.guidESharpProjectCmdSet)
            {
                // Handle commands on E# file nodes!
            }
            return base.ExecCommandOnNode(guidCmdGroup, cmd, nCmdexecopt, pvaIn, pvaOut);
        }

        /// <summary>
        /// Handles the menuitems
        /// </summary>		
        protected override int QueryStatusOnNode(Guid guidCmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result)
        {
            if (guidCmdGroup == Microsoft.VisualStudio.Shell.VsMenus.guidStandardCommandSet97)
            {
                switch ((VsCommands)cmd)
                {
                    case VsCommands.AddClass:
                    case VsCommands.AddNewItem:
                    case VsCommands.AddExistingItem:
                    case VsCommands.ViewCode:
                        result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED;
                        return VSConstants.S_OK;
                    case VsCommands.ViewForm:
                        if (IsFormSubType)
                            result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED;
                        return VSConstants.S_OK;
                }
            }

            else if (guidCmdGroup == ESharpMenus.guidESharpProjectCmdSet)
            {
                // Handle commands for E# file nodes...
            }
            return base.QueryStatusOnNode(guidCmdGroup, cmd, pCmdText, ref result);
        }

        #endregion

        #region methods
        public string GetRelativePath()
        {
            string relativePath = Path.GetFileName(this.ItemNode.GetMetadata(ProjectFileConstants.Include));
            HierarchyNode parent = this.Parent;
            while (parent != null && !(parent is ProjectNode))
            {
                relativePath = Path.Combine(parent.Caption, relativePath);
                parent = parent.Parent;
            }
            return relativePath;
        }

        internal OleServiceProvider.ServiceCreatorCallback ServiceCreator
        {
            get { return new OleServiceProvider.ServiceCreatorCallback(this.CreateServices); }
        }

        private object CreateServices(Type serviceType)
        {
            object service = null;
            if (typeof(EnvDTE.ProjectItem) == serviceType)
            {
                service = GetAutomationObject();
            }
            return service;
        }
        #endregion
    }
}
